﻿using Abp.Extensions;
using Abp.Net.Mail;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SportsPlatform.BaseModels;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Security.Policy;
using System.Web;
using Abp.Logging;
using System.Diagnostics;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using NPOI.POIFS.Crypt.Dsig;
using System.Threading.Tasks;

namespace SportsPlatform.Helpers
{
    /// <summary>
    /// 通用帮助类
    /// </summary>
    public static class CommonHelper
    {
        #region 通过身份证号码获取相关信息

        /// <summary>
        /// 从身份证号中提取生日
        /// </summary>
        /// <param name="idNumber">身份证号</param>
        /// <returns>结果</returns>
        public static DateTime? ExtracBorthFromIdNumber(this string idNumber)
        {
            if (idNumber.IsNullOrEmpty() || !Regex.IsMatch(idNumber, @"(^\d{15}$)|(^\d{17}[\dXx]$)"))
                return null;
            if (idNumber.Length == 15)
            {
                if (DateTime.TryParseExact($"19{idNumber.Substring(6, 6)}", "yyyyMMdd", Thread.CurrentThread.CurrentUICulture, System.Globalization.DateTimeStyles.None, out var birth))
                    return birth;
                return null;
            }
            if (DateTime.TryParseExact(idNumber.Substring(6, 8), "yyyyMMdd", Thread.CurrentThread.CurrentUICulture, System.Globalization.DateTimeStyles.None, out var birthday))
                return birthday;
            return null;
        }

        /// <summary>
        /// 从身份证号获取年龄
        /// </summary>
        /// <param name="idNumber">身份证号</param>
        /// <returns>结果</returns>
        public static int? GetAgeFromIdNumber(this string idNumber)
        {
            var birthday = ExtracBorthFromIdNumber(idNumber);
            if (!birthday.HasValue)
                return null;
            return GetAge(birthday.Value);
        }

        /// <summary>
        /// 从身份证中判断是否为男性
        /// </summary>
        /// <param name="idNumber">身份证号</param>
        /// <returns>结果</returns>
        public static bool? IsManFromIdNumber(this string idNumber)
        {
            if (idNumber.IsNullOrEmpty() || !Regex.IsMatch(idNumber, @"(^\d{15}$)|(^\d{17}[\dXx]$)"))
                return null;
            if (idNumber.Length == 15)
                return int.Parse(idNumber[14].ToString()) % 2 == 1;
            return int.Parse(idNumber[16].ToString()) % 2 == 1;
        }

        /// <summary>
        /// 获取年龄
        /// </summary>
        /// <param name="birthday">生日</param>
        /// <returns>结果</returns>
        public static int GetAge(this DateTime birthday)
        {
            var today = DateTime.Today;
            int age = today.Year - birthday.Year;
            //二月要特殊处理
            if (today.Month == 2 && birthday.Month == 2)
            {
                //如果生日是二月的最后一天，则对比今天是否是二月的最后一天，如果不是则年龄-1
                if (birthday.AddDays(1).Month == 3 && today.AddDays(1).Month == 2)
                    age--;
                return age;
            }
            if (today.Month < birthday.Month || (today.Month == birthday.Month && today.Day < birthday.Day))
                age--;
            return age;
        }

        /// <summary>
        /// 获取性别
        /// </summary>
        /// <param name="idNumber">身份证号</param>
        /// <returns>性别</returns>
        public static string GetSex(this string idNumber)
        {
            var s = idNumber.Substring(16, 1);
            int num = 0;
            if (!int.TryParse(s, out num))
            {
                return "女";
            }
            else
            {
                return int.Parse(s) % 2 == 0 ? "女" : "男";

            }
        }

        #endregion

        #region Json转换

        /// <summary>
        /// 对象转Json
        /// </summary>
        /// <typeparam name="T">要转换的对象泛型</typeparam>
        /// <param name="source">要转换的对象</param>
        /// <returns>转换后的字符串</returns>
        public static string ToJson<T>(T source)
        {
            if (null == source)
                return null;
            if (source is string str)
                return str;
            try
            {
                return JsonConvert.SerializeObject(source);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Json转换为对象
        /// </summary>
        /// <typeparam name="T">要转换成的泛型</typeparam>
        /// <param name="json">Json数据</param>
        /// <returns>转换后的对象</returns>
        public static T JsonToObject<T>(string json)
        {
            try
            {
                if (typeof(T).Name == "String")
                {
                    return (T)(object)json;
                }
                return JsonConvert.DeserializeObject<T>(json);
            }
            catch
            {
                return default;
            }
        }

        /// <summary>
        /// Json转换为对象
        /// </summary>
        /// <param name="json">Json数据</param>
        /// <param name="type">要转换的类型</param>
        /// <returns>转换后的对象</returns>
        public static object JsonToObject(string json, Type type)
        {
            return JsonConvert.DeserializeObject(json, type);
        }

        #endregion

        #region  验证格式扩展方法

        /// <summary>
        /// 是否为null或空
        /// </summary>
        /// <param name="source">要检查的对象</param>
        /// <returns>结果</returns>
        public static bool IsNullAndEmpty(this object source)
        {
            if (ReferenceEquals(null, source))
                return true;
            if (source is string)
                return string.IsNullOrWhiteSpace((string)source);
            return false;
        }

        /// <summary>
        /// 验证Guid
        /// </summary>
        /// <param name="strSrc"></param>
        /// <returns></returns>
        public static bool IsGuidByParse(this string strSrc)
        {
            Guid g = Guid.Empty;
            return Guid.TryParse(strSrc, out g);
        }

        /// <summary>
        /// 正则表达式检测Email格式
        /// </summary>
        /// <param name="Email"></param>
        /// <returns></returns>
        public static bool IsEmail(this string Email)
        {
            bool Flag = false;
            string str = "([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,5})+";
            string[] result = GetPathPoint(Email, str);
            if (result != null)
            {
                Flag = result.Contains(Email) ? true : Flag;
            }
            return Flag;
        }

        /// <summary>
        /// 获取正则表达式匹配结果集
        /// </summary>
        /// <param name="value">字符串</param>
        /// <param name="regx">正则表达式</param>
        /// <returns></returns>
        public static string[] GetPathPoint(string value, string regx)
        {
            if (string.IsNullOrWhiteSpace(value))
                return null;
            bool isMatch = System.Text.RegularExpressions.Regex.IsMatch(value, regx);
            if (!isMatch)
                return null;
            System.Text.RegularExpressions.MatchCollection matchCol = System.Text.RegularExpressions.Regex.Matches(value, regx);
            string[] result = new string[matchCol.Count];
            if (matchCol.Count > 0)
            {
                for (int i = 0; i < matchCol.Count; i++)
                {
                    result[i] = matchCol[i].Value;
                }
            }
            return result;
        }
        #endregion

        #region 加密扩展方法

        /// <summary>
        /// des向量
        /// </summary>
        private static readonly byte[] desIV = { 0x72, 0x86, 0x63, 0x56, 0x88, 0xAB, 0xCD, 0xAB };
        /// <summary>
        /// AES向量
        /// </summary>
        private static readonly string aesIV = "vmy#79VK^P10t%4y";
        private static readonly string aesKey = "0^!Nzk*e1h0|hkji$bP5S,xNx7?x9xQJ&";

        /// <summary>
        /// DES加密字符串
        /// </summary>
        /// <param name="encryptString">待加密的字符串</param>
        /// <param name="encryptKey">加密密钥,要求为8位</param>
        /// <returns>加密成功返回加密后的字符串，失败返回源串</returns>
        public static string EncryptDES(string encryptString, string encryptKey)
        {
            try
            {
                encryptKey = (encryptKey ?? "").PadRight(8, 'a').Substring(0, 8);
                var rgbKey = Encoding.UTF8.GetBytes(encryptKey);
                var rgbIV = desIV;
                var inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                var dCSP = new DESCryptoServiceProvider();
                using (var mStream = new MemoryStream())
                using (var cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write))
                {
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    return Convert.ToBase64String(mStream.ToArray());
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// DES解密字符串
        /// </summary>
        /// <param name="decryptString">待解密的字符串</param>
        /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>
        /// <returns>解密成功返回解密后的字符串，失败返源串</returns>
        public static string DecryptDES(string decryptString, string decryptKey)
        {
            try
            {
                decryptKey = (decryptKey ?? "").PadRight(8, 'a').Substring(0, 8);
                var rgbKey = Encoding.UTF8.GetBytes(decryptKey);
                var rgbIV = desIV;
                var inputByteArray = Convert.FromBase64String(decryptString);
                using (var dcsp = new DESCryptoServiceProvider())
                using (var mStream = new MemoryStream())
                using (var cStream = new CryptoStream(mStream, dcsp.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write))
                {
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    return Encoding.UTF8.GetString(mStream.ToArray());
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary> 
        /// AES加密 
        /// </summary>
        public static string AesEncrypt(string value, string _aeskey)
        {
            byte[] keyArray = Encoding.UTF8.GetBytes(_aeskey);
            byte[] toEncryptArray = Encoding.UTF8.GetBytes(value);

            RijndaelManaged rDel = new RijndaelManaged
            {
                Key = keyArray,
                Mode = CipherMode.ECB,
                Padding = PaddingMode.PKCS7
            };

            ICryptoTransform cTransform = rDel.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }

        /// <summary> 
        /// AES解密 
        /// </summary>
        public static string AesDecrypt(string value, string _aeskey)
        {
            try
            {
                byte[] keyArray = Encoding.UTF8.GetBytes(_aeskey);
                byte[] toEncryptArray = Convert.FromBase64String(value);

                RijndaelManaged rDel = new RijndaelManaged
                {
                    Key = keyArray,
                    Mode = CipherMode.ECB,
                    Padding = PaddingMode.PKCS7
                };

                ICryptoTransform cTransform = rDel.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

                return Encoding.UTF8.GetString(resultArray);
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="content">要加密的内容</param>
        /// <returns>加密后的内容</returns>
        public static string MD5Encryption(string content,int number=16)
        {
            if (content.IsNullOrEmpty())
                return content;
            //16位大写
            using (var md5 = MD5.Create())
            {
                var result = md5.ComputeHash(Encoding.UTF8.GetBytes(content));
                var strResult = BitConverter.ToString(result);
                string result3 = strResult.Replace("-", "");
                return number == 16 ? result3.Substring(8, 16) : result3;
            }
        }
        /// <summary>
        /// 获取Rsa密钥对
        /// </summary>
        /// <returns>Item1私钥，Item2公钥</returns>
        public static Tuple<string, string> GetRsaKeyPair()
        {
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            string publicKey = RSA.ToXmlString(false);
            string privateKey = RSA.ToXmlString(true);
            return new Tuple<string, string>(privateKey, publicKey);
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="content">要加密的内容</param>
        /// <param name="publickey">公钥</param>
        /// <returns></returns>
        public static string RSAEncrypt(string content, string publickey)
        {
            var rsa = new RSACryptoServiceProvider();
            byte[] cipherbytes;
            rsa.FromXmlStringExtensions(publickey);
            cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(content), false);
            return Convert.ToBase64String(cipherbytes);
        }

        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="content">要解密的内容</param>
        /// <param name="privatekey">私钥</param>
        /// <returns></returns>
        public static string RSADecrypt(string content, string privatekey)
        {
            var rsa = new RSACryptoServiceProvider();
            byte[] cipherbytes;
            rsa.FromXmlStringExtensions(privatekey);
            cipherbytes = rsa.Decrypt(Convert.FromBase64String(content), false);
            return Encoding.UTF8.GetString(cipherbytes);
        }

        #endregion

        #region 分页扩展方法
        public static IEnumerable<T> ToPage<T>(this IEnumerable<T> values, int pageIndex, int pageSize) where T : class
        {
            return values.Skip((pageIndex - 1) * pageSize).Take(pageSize);
        }
        #endregion

        #region 时间扩张方法

        /// <summary>
        /// 验证当前时间是否是同一天 true:同一天 false:不是同一天
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static bool IsToday(this DateTime dt)
        {
            DateTime today = DateTime.Today;
            DateTime tempToday = new DateTime(dt.Year, dt.Month, dt.Day);
            if (today == tempToday)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 判断时间属于星期几
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static string DayToWeek(this DateTime dt)
        {
            string ret = "";
            switch (dt.DayOfWeek)
            {
                case DayOfWeek.Sunday: ret = "星期天"; break;
                case DayOfWeek.Monday: ret = "星期一"; break;
                case DayOfWeek.Tuesday: ret = "星期二"; break;
                case DayOfWeek.Wednesday: ret = "星期三"; break;
                case DayOfWeek.Thursday: ret = "星期四"; break;
                case DayOfWeek.Friday: ret = "星期五"; break;
                case DayOfWeek.Saturday: ret = "星期六"; break;
            }
            return ret;
        }

        #endregion

        #region 请求外部接口扩展方法

        /// <summary>
        /// 指定Post地址使用Get 方式获取全部字符串
        /// Post是从服务器上传送数据
        /// </summary>
        /// <param name="url">请求Url地址</param>
        /// <param name="dic">拼接Url的字段</param>
        /// <returns></returns>
        public async static Task<JObject> PostAsync(this string url, string data)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

            request.Method = "POST";
            request.ContentType = "application/json";
            //string strContent = "{ \"account\": \"" + account + "\",\"password\": \"" + pwd + "\"}";
            string strContent = data; //参数data的格式就是上一句被注释的语句
            using (StreamWriter dataStream = new StreamWriter(request.GetRequestStream()))
            {
                dataStream.Write(strContent);
                dataStream.Close();
            }
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            string encoding = response.ContentEncoding;
            if (encoding == null || encoding.Length < 1)
            {
                encoding = "UTF-8"; //默认编码  
            }
            JObject jo = new JObject();
            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encoding)))
            {
                string retString = await reader.ReadToEndAsync();
                jo = JObject.Parse(retString);
            }

            //解析josn
            //sessionId = jo["sessionId"].ToString();
            //return sessionid;
            return jo;

        }

        public async static Task<string> GetAsync(this string url)
        {
            //访问http方法
            string strBuff = "";
            Uri httpURL = new Uri(url);

            ///HttpWebRequest类继承于WebRequest，并没有自己的构造函数，需通过WebRequest的Creat方法建立，并进行强制的类型转换   
            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(httpURL);
            ///通过HttpWebRequest的GetResponse()方法建立HttpWebResponse,强制类型转换   
            var httpres = httpReq.GetResponse();
            HttpWebResponse httpResp = (HttpWebResponse)httpres;
            ///GetResponseStream()方法获取HTTP响应的数据流,并尝试取得URL中所指定的网页内容   
            ///若成功取得网页的内容，则以System.IO.Stream形式返回，若失败则产生ProtoclViolationException错误。在此正确的做法应将以下的代码放到一个try块中处理。这里简单处理   
            Stream respStream = httpResp.GetResponseStream();
            ///返回的内容是Stream形式的，所以可以利用StreamReader类获取GetResponseStream的内容，并以   
            //StreamReader类的Read方法依次读取网页源程序代码每一行的内容，直至行尾（读取的编码格式：UTF8）   
            StreamReader respStreamReader = new StreamReader(respStream, Encoding.Default);
            strBuff = await respStreamReader.ReadToEndAsync();
            return strBuff;
        }

        #endregion
    }

    /// <summary>
    /// System.Security.Cryptography.RSA 扩展方法
    /// </summary>
    internal static class RSAExtensions
    {
        // 处理 下面两种方式都会出现的 Operation is not supported on this platform 异常
        // RSA.Create().FromXmlString(privateKey) 
        // new RSACryptoServiceProvider().FromXmlString(privateKey) 

        /// <summary>
        /// 扩展FromXmlString
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="xmlString"></param>
        public static void FromXmlStringExtensions(this RSA rsa, string xmlString)
        {
            RSAParameters parameters = new RSAParameters();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);

            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
            {
                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                        case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    }
                }
            }
            else
            {
                throw new Exception("Invalid XML RSA key.");
            }

            rsa.ImportParameters(parameters);
        }

        /// <summary>
        /// 扩展ToXmlString
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="includePrivateParameters"></param>
        /// <returns></returns>
        public static string ToXmlStringExtensions(this RSA rsa, bool includePrivateParameters)
        {
            RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);

            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                  parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
                  parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
                  parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
                  parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
                  parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
                  parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
                  parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
                  parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
        }
    }
}
